package it.unipd.fbaron.mind_reader_squared.model;

import it.unipd.fbaron.mind_reader_squared.R;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import android.content.ContentValues;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteException;

public class SymbolLogic {

	// Singleton
	private static SymbolLogic istance;

	// DB Helper
	private DataBaseHelper dbHelper;

	private SymbolLogic(DataBaseHelper dbHelper) {
		this.dbHelper = dbHelper;
	}

	public static SymbolLogic getInstance(DataBaseHelper dbHelper) {
		if (istance == null) {
			istance = new SymbolLogic(dbHelper);
		}
		return istance;
	}
	
	public SymbolDAO getSymbolById(int id) {
		if (id <= 0)
			return null;
		
		Cursor result;
		try {
			result = dbHelper.getReadableDatabase().rawQuery(
					"SELECT * FROM "
							+ DataBaseHelper.SYMBOL_TABLE_NAME
							+ " WHERE id=" + id, null);
		} catch (SQLiteException e) {
			e.printStackTrace();
			return null;
		}
		
		result.moveToFirst();
		
		boolean m1 = false, m2 = false;

		if (result.getInt(8) > 0)
			m2 = true;
		if (result.getInt(6) > 0)
			m1 = true;

		int parent = 0;
		try {
			parent = result.getInt(5);
		} catch (Exception e) {
		}

		SymbolDAO s = new SymbolDAO(result.getInt(0), result.getString(1),
				result.getInt(2), result.getString(3), result.getString(4),
				parent, m1, result.getInt(7), m2, result.getInt(9),
				result.getString(10), result.getInt(11), result.getInt(13));
		s.setClicksM1(result.getInt(12));
		s.setClicksM2(result.getInt(14));
		return s;
	}

	public ArrayList<SymbolDAO> getM1SymbolList(int profile_id) {
		Cursor result;
		try {
			result = dbHelper.getReadableDatabase().rawQuery(
					"SELECT DISTINCT * FROM "
							+ DataBaseHelper.SYMBOL_TABLE_NAME
							+ " WHERE m1=1 AND profile=" + profile_id, null);
		} catch (SQLiteException e) {
			e.printStackTrace();
			return null;
		}

		ArrayList<SymbolDAO> list = new ArrayList<SymbolDAO>();
		while (result.moveToNext()) {
			boolean m1 = true, m2 = false;

			if (result.getInt(8) > 0)
				m2 = true;

			int parent = 0;
			try {
				parent = result.getInt(5);
			} catch (Exception e) {
			}

			SymbolDAO s = new SymbolDAO(result.getInt(0), result.getString(1),
					result.getInt(2), result.getString(3), result.getString(4),
					parent, m1, result.getInt(7), m2, result.getInt(9),
					result.getString(10), result.getInt(11), result.getInt(13));
			s.setClicksM1(result.getInt(12));
			s.setClicksM2(result.getInt(14));
			list.add(s);
		}

		Collections.sort(list, new SymbolComparatorPosM1());
		return list;
	}

	//Return all the symbols if input parent is -1
	public ArrayList<SymbolDAO> getM2SymbolList(int profile_id, int parent) {
		String parent_str = String.valueOf(parent);
		if (parent == 0)
			parent_str = "'NULL'";
		
		Cursor result;
		try {
			if (parent == -1)
				result = dbHelper.getReadableDatabase().rawQuery(
						"SELECT DISTINCT * FROM "
								+ DataBaseHelper.SYMBOL_TABLE_NAME
								+ " WHERE profile=" + profile_id + " AND m2=1", null);
			else
				result = dbHelper.getReadableDatabase().rawQuery(
						"SELECT DISTINCT * FROM "
								+ DataBaseHelper.SYMBOL_TABLE_NAME
								+ " WHERE profile=" + profile_id + " AND m2=1 AND parent=" + parent_str, null);
		} catch (SQLException e) {
			return null;
		}

		ArrayList<SymbolDAO> list = new ArrayList<SymbolDAO>();
		while (result.moveToNext()) {

			boolean m1 = false, m2 = true;

			if (result.getInt(6) > 0)
				m1 = true;

			SymbolDAO s = new SymbolDAO(result.getInt(0), result.getString(1),
					result.getInt(2), result.getString(3), result.getString(4),
					parent, m1, result.getInt(7), m2, result.getInt(9),
					result.getString(10), result.getInt(11), result.getInt(13));
			s.setClicksM1(result.getInt(12));
			s.setClicksM2(result.getInt(14));
			list.add(s);
		}

		Collections.sort(list, new SymbolComparatorPosM2());
		return list;
	}
	
	public int getSymbolNumChildren (int symbol_id) {
		String id_string = "";
		if (symbol_id <= 0) {
			id_string = "'NULL'";
		}
		else {
			id_string = String.valueOf(symbol_id);
		}
		
		Cursor result;
		try {
			result = dbHelper.getReadableDatabase().rawQuery(
					"SELECT COUNT(id) FROM "
							+ DataBaseHelper.SYMBOL_TABLE_NAME
							+ " WHERE m2=1 AND parent=" + id_string + " GROUP BY parent", null);
		} catch (SQLException e) {
			return 0;
		}
		
		if (result.getCount() > 0) {
			result.moveToFirst();
			return result.getInt(0);
		}
		else
			return 0;
	}

	public void deleteSymbol(final int profile_id, final int symbol_id, final boolean m1,
			final boolean m2) {
		Cursor result;
		if (m1 && m2) {
			// Recalculation of symbols positions
			result = dbHelper.getReadableDatabase().rawQuery(
					"SELECT profile,m1_pos,m2_pos,parent FROM "
							+ DataBaseHelper.SYMBOL_TABLE_NAME + " WHERE ID="
							+ symbol_id, null);
			result.moveToFirst();
			int profile = result.getInt(0);
			int m1_pos = result.getInt(1);
			int m2_pos = result.getInt(2);
			int parent = result.getInt(3);
			String parent_str = (parent == 0) ? "'NULL'" : String.valueOf(parent);
			// M1 position
			if (m1_pos > -1) {
				int max = getNumM1Symbols(profile);
				for (int i = m1_pos + 1; i <= max; i++) {
					try {
						ContentValues cv = new ContentValues();
						cv.put("m1_pos", i - 1);
						dbHelper.getWritableDatabase()
								.update(DataBaseHelper.SYMBOL_TABLE_NAME,
										cv,
										"profile=" + profile + " AND m1_pos="
												+ i, null);
					} catch (SQLiteException e) {
						i = max + 1;
					}
				}
			}
			// M2 position
			if (m2_pos > -1) {
				int max = getSymbolNumChildren(parent);
				for (int i = m2_pos + 1; i <= max; i++) {
					try {
						ContentValues cv = new ContentValues();
						cv.put("m2_pos", i - 1);
						dbHelper.getWritableDatabase()
								.update(DataBaseHelper.SYMBOL_TABLE_NAME,
										cv,
										"profile=" + profile + " AND m2_pos="
												+ i + " AND parent=" + parent_str, null);
					} catch (SQLiteException e) {
						i = max + 1;
					}
				}
			}

			// Delete symbol from database
			dbHelper.getWritableDatabase().delete(
					DataBaseHelper.SYMBOL_TABLE_NAME, "ID=" + symbol_id, null);
		} else {
			if (m1) {
				// Recalculation of symbols positions
				result = dbHelper.getReadableDatabase().rawQuery(
						"SELECT profile,m1_pos FROM "
								+ DataBaseHelper.SYMBOL_TABLE_NAME
								+ " WHERE ID=" + symbol_id, null);
				result.moveToFirst();
				int profile = result.getInt(0);
				int m1_pos = result.getInt(1);
				// M1 position
				if (m1_pos > -1) {
					int max = getNumM1Symbols(profile);
					for (int i = m1_pos + 1; i <= max; i++) {
						try {
							ContentValues cv = new ContentValues();
							cv.put("m1_pos", i - 1);
							dbHelper.getWritableDatabase().update(
									DataBaseHelper.SYMBOL_TABLE_NAME, cv,
									"profile=" + profile + " AND m1_pos=" + i,
									null);
						} catch (SQLiteException e) {
							i = max + 1;
						}
					}
				}

				ContentValues cv = new ContentValues();
				cv.put("m1", 0);
				cv.put("m1_pos", -1);

				dbHelper.getWritableDatabase().update(
						DataBaseHelper.SYMBOL_TABLE_NAME, cv,
						"ID=" + symbol_id, null);
			}
			if (m2) {
				// Recalculation of symbols positions
				result = dbHelper.getReadableDatabase().rawQuery(
						"SELECT profile,m2_pos,parent FROM "
								+ DataBaseHelper.SYMBOL_TABLE_NAME
								+ " WHERE ID=" + symbol_id, null);
				result.moveToFirst();
				int profile = result.getInt(0);
				int m2_pos = result.getInt(1);
				int parent = result.getInt(2);
				String parent_str = (parent == 0) ? "'NULL'" : String.valueOf(parent);
				// M2 position
				if (m2_pos > -1) {
					int max = getSymbolNumChildren(parent);
					for (int i = m2_pos + 1; i <= max; i++) {
						try {
							ContentValues cv = new ContentValues();
							cv.put("m2_pos", i - 1);
							dbHelper.getWritableDatabase().update(
									DataBaseHelper.SYMBOL_TABLE_NAME, cv,
									"profile=" + profile + " AND m2_pos=" + i +
									" AND parent=" + parent_str,
									null);
						} catch (SQLiteException e) {
							i = max + 1;
						}
					}
				}

				ContentValues cv = new ContentValues();
				cv.put("m2", 0);
				cv.put("m2_pos", -1);

				dbHelper.getWritableDatabase().update(
						DataBaseHelper.SYMBOL_TABLE_NAME, cv,
						"ID=" + symbol_id, null);
				
				//Delete children recursive
				deleteChildrenRecursive(profile_id, symbol_id);
			}

			result = dbHelper.getReadableDatabase().rawQuery(
					"SELECT m1,m2 FROM " + DataBaseHelper.SYMBOL_TABLE_NAME
							+ " WHERE ID=" + symbol_id, null);
			result.moveToFirst();
			if (result.getInt(0) <= 0 && result.getInt(1) <= 0) {
				dbHelper.getWritableDatabase().delete(
						DataBaseHelper.SYMBOL_TABLE_NAME, "ID=" + symbol_id,
						null);
			}
		}
	}
	
	private void deleteChildrenRecursive(int profile_id, int symbol_id) {
		ArrayList<SymbolDAO> children_list = getM2SymbolList(profile_id, symbol_id);
		
		if(children_list.size() == 0)
			return;
		
		for (SymbolDAO s:children_list) {
			if (s.getM1Pos() >= 0) {
				ContentValues cv = new ContentValues();
				cv.put("m2", 0);
				cv.put("m2_pos", -1);
				cv.put("parent", "NULL");
				dbHelper.getWritableDatabase().update(
						DataBaseHelper.SYMBOL_TABLE_NAME, cv,
						"ID=" + s.getId(), null);
				deleteChildrenRecursive(profile_id, s.getId());
			} else {
				dbHelper.getWritableDatabase().delete(
						DataBaseHelper.SYMBOL_TABLE_NAME, "ID=" + s.getId(),
						null);
			}
		}
	}

	public void saveChanges(final SymbolDAO symbol) {
		int m1 = 0, m2 = 0;
		if (symbol.isM1())
			m1 = 1;
		if (symbol.isM2())
			m2 = 1;

		String parent = "NULL";
		if (symbol.getParent() > 0)
			parent = String.valueOf(symbol.getParent());

		ContentValues cv = new ContentValues();
		cv.put("name", symbol.getName());
		cv.put("image_resource", symbol.getImageResource());
		cv.put("text", DataBaseHelper.cleanStringForSave(symbol.getText()));
		cv.put("image_path", symbol.getImagePath());
		cv.put("parent", parent);
		cv.put("m1", m1);
		cv.put("m1_pos", symbol.getM1Pos());
		cv.put("m2", m2);
		cv.put("m2_pos", symbol.getM2Pos());
		cv.put("sound_path", symbol.getSoundPath());
		cv.put("profile", symbol.getProfileId());
		cv.put("clicks_m1", symbol.getClicksM1());
		cv.put("sound_choise", symbol.getSoundChoise());
		cv.put("clicks_m2", symbol.getClicksM2());

		dbHelper.getWritableDatabase().update(DataBaseHelper.SYMBOL_TABLE_NAME,
				cv, "ID=" + symbol.getId(), null);
	}

	public int newSymbol(final SymbolDAO symbol) {
		int m1 = 0, m2 = 0;
		if (symbol.isM1())
			m1 = 1;
		if (symbol.isM2())
			m2 = 1;

		String parent = "NULL";
		if (symbol.getParent() > 0)
			parent = String.valueOf(symbol.getParent());

		ContentValues cv = new ContentValues();
		cv.put("name", symbol.getName());
		cv.put("image_resource", symbol.getImageResource());
		cv.put("text", DataBaseHelper.cleanStringForSave(symbol.getText()));
		cv.put("image_path", symbol.getImagePath());
		cv.put("parent", parent);
		cv.put("m1", m1);
		cv.put("m1_pos", symbol.getM1Pos());
		cv.put("m2", m2);
		cv.put("m2_pos", symbol.getM2Pos());
		cv.put("sound_path", symbol.getSoundPath());
		cv.put("profile", symbol.getProfileId());
		cv.put("clicks_m1", symbol.getClicksM1());
		cv.put("sound_choise", symbol.getSoundChoise());
		cv.put("clicks_m2", symbol.getClicksM2());

		return (int) dbHelper.getWritableDatabase().insert(
				DataBaseHelper.SYMBOL_TABLE_NAME, null, cv);
	}

	public boolean isSymbolDescendant(int profile_id, int parent, int descendant) {
		ArrayList<SymbolDAO> children_list = getM2SymbolList(profile_id, parent);
		
		if(children_list.size() == 0)
			return false;
		
		for (SymbolDAO s:children_list) {
			if (s.getId() == descendant)
				return true;
			else
				return isSymbolDescendant(profile_id, s.getId(), descendant);
		}
		return false;
	}
	
	public void saveClicks(final int symbol_id, final int n_clicks, boolean mode) {
		int oldValue;
		Cursor result;
		if (mode)
			result = dbHelper.getReadableDatabase().rawQuery(
					"SELECT clicks_m1 FROM " + DataBaseHelper.SYMBOL_TABLE_NAME
							+ " WHERE ID=" + symbol_id, null);
		else
			result = dbHelper.getReadableDatabase().rawQuery(
					"SELECT clicks_m2 FROM " + DataBaseHelper.SYMBOL_TABLE_NAME
							+ " WHERE ID=" + symbol_id, null);

		if (result.moveToFirst()) {
			oldValue = result.getInt(0);
		} else {
			oldValue = 0;
		}

		int newValue = oldValue + n_clicks;

		ContentValues cv = new ContentValues();
		if (mode)
			cv.put("clicks_m1", newValue);
		else
			cv.put("clicks_m2", newValue);
		dbHelper.getWritableDatabase().update(DataBaseHelper.SYMBOL_TABLE_NAME,
				cv, "ID=" + symbol_id, null);
	}

	public int getClicks(int symbol_id, boolean mode) {
		Cursor result;
		try {
			if (mode)
				result = dbHelper.getReadableDatabase().rawQuery(
						"SELECT clicks_m1 FROM "
								+ DataBaseHelper.SYMBOL_TABLE_NAME
								+ " WHERE ID=" + symbol_id, null);
			else
				result = dbHelper.getReadableDatabase().rawQuery(
						"SELECT clicks_m2 FROM "
								+ DataBaseHelper.SYMBOL_TABLE_NAME
								+ " WHERE ID=" + symbol_id, null);
		} catch (SQLiteException e) {
			return 0;
		}

		int value = 0;
		if (result.moveToFirst()) {
			value = result.getInt(0);
		}

		return value;
	}

	public int getNumM1Symbols(int profile_id) {
		Cursor result;
		try {
			result = dbHelper.getReadableDatabase().rawQuery(
					"SELECT ID FROM " + DataBaseHelper.SYMBOL_TABLE_NAME
							+ " WHERE m1=1 AND profile=" + profile_id, null);
		} catch (SQLiteException e) {
			return -1;
		}
		return result.getCount();
	}

	public int getNumM2Symbols(int profile_id) {
		Cursor result;
		try {
			result = dbHelper.getReadableDatabase().rawQuery(
					"SELECT ID FROM " + DataBaseHelper.SYMBOL_TABLE_NAME
							+ " WHERE m2=1 AND profile=" + profile_id, null);
		} catch (SQLiteException e) {
			return -1;
		}
		return result.getCount();
	}

	public void saveSymbolsPos(ArrayList<SymbolDAO> symbol_list, boolean mode) {
		if (!mode) {			
			int i = -1;
			for (SymbolDAO s:symbol_list) {
				if (s.getM2Pos() > i+1)
					s.setM2Pos(i+1);
				i = s.getM2Pos();
			}
		}
		for (int i = 0; i < symbol_list.size(); i++) {
			ContentValues cv = new ContentValues();
			if (mode)
				cv.put("m1_pos", symbol_list.get(i).getM1Pos());
			else
				cv.put("m2_pos", symbol_list.get(i).getM2Pos());

			dbHelper.getWritableDatabase().update(
					DataBaseHelper.SYMBOL_TABLE_NAME, cv,
					"ID=" + symbol_list.get(i).getId(), null);
		}
	}

	public void resetStandardSymbolsTexts(Context context, int profile_id) {
		ContentValues cv;
		TypedArray symbolList = context.getResources().obtainTypedArray(
				R.array.m1_symbol_list);
		TypedArray defaultTexts = context.getResources().obtainTypedArray(
				R.array.m1_symbols_default_texts);

		for (int i = 0; i < symbolList.length(); i++) {
			cv = new ContentValues();
			cv.put("text", DataBaseHelper.cleanStringForSave(context
					.getResources().getString(
							defaultTexts.getResourceId(i,
									R.string.m1_text_not_found))));
			dbHelper.getWritableDatabase().update(
					DataBaseHelper.SYMBOL_TABLE_NAME,
					cv,
					"profile=" + profile_id + " AND name='"
							+ symbolList.getString(i) + "'", null);
		}
		symbolList.recycle();
		defaultTexts.recycle();
	}

	public static class SymbolComparatorPosM1 implements Comparator<SymbolDAO> {

		@Override
		public int compare(SymbolDAO object1, SymbolDAO object2) {
			if (object1.getM1Pos() == object2.getM1Pos())
				return 0;
			if (object1.getM1Pos() < object2.getM1Pos())
				return -1;
			else
				return 1;
		}

	}

	public static class SymbolComparatorPosM2 implements Comparator<SymbolDAO> {

		@Override
		public int compare(SymbolDAO object1, SymbolDAO object2) {
			if (object1.getM2Pos() == object2.getM2Pos())
				return 0;
			if (object1.getM2Pos() < object2.getM2Pos())
				return -1;
			else
				return 1;
		}

	}

	public static class SymbolComparatorClicksM1 implements
			Comparator<SymbolDAO> {

		@Override
		public int compare(SymbolDAO object1, SymbolDAO object2) {
			if (object1.getClicksM1() == object2.getClicksM1())
				return 0;
			if (object1.getClicksM1() < object2.getClicksM1())
				return -1;
			else
				return 1;
		}

	}

	public static class SymbolComparatorClicksM2 implements
			Comparator<SymbolDAO> {

		@Override
		public int compare(SymbolDAO object1, SymbolDAO object2) {
			if (object1.getClicksM2() == object2.getClicksM2())
				return 0;
			if (object1.getClicksM2() < object2.getClicksM2())
				return -1;
			else
				return 1;
		}

	}
}
